﻿using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Windows;

namespace LunarSF.SHomeWorkshop.LunarMarkdownEditor
{
    public class CustomMarkdownSupport
    {
        /// <summary>
        /// 在使用markDown转换后，再对所有h1进行处理，自动生成jQuery函数，这样就可以支持点击标题显示、隐藏文本了。
        /// </summary>
        /// <param name="htmlString"></param>
        /// <returns></returns>
        public static string AppendProcessor(string htmlString, string directoryMark = "./", bool collapseH1 = false, bool fillBlankOn = true)
        {
            if (string.IsNullOrEmpty(htmlString)) return "";

            //载入jquery库。这东西无论如何都应载入，且只应载入一次。
            StringBuilder javascriptBuilder = new StringBuilder();
            //jQueryStringBuilder.Append("<script src=\"" + directoryMark + "json2.js\"></script>");
            javascriptBuilder.Append("<script src=\"" + directoryMark + "jquery-1.7.0.min.js\"></script>" +
                    "<script src=\"" + directoryMark + "jquery.jqprint-0.3.js\"></script>" +
                    "<script language=\"javascript\">" +
                        "function print() {" +
                           "$(\"#printArea\").jqprint({" +
                                   "importCSS: true" +
                           "});" +
                         "}" +
                    "</script>");

            if (fillBlankOn)
            {
                #region 添加填空模式的js代码

                javascriptBuilder.Append("<script>");
                javascriptBuilder.Append("$(document).ready(function(){$('p code').css('color', \"transparent\");});");
                javascriptBuilder.Append("$(document).ready(function(){$('li code').css('color', \"transparent\");});");
                javascriptBuilder.Append("$(document).ready(function(){$('tr code').css('color', \"transparent\");});");
                javascriptBuilder.Append("$(document).ready(function(){$('.panel code').css('color', \"transparent\");});");
                javascriptBuilder.Append("$(document).ready(function() {");
                javascriptBuilder.Append("$('p').click(function() {");
                javascriptBuilder.Append("var mycss = document.getElementById('themeLink');");
                javascriptBuilder.Append("if (mycss.getAttribute('href').indexOf('lesson_dark.css')>0)");
                javascriptBuilder.Append("{");
                javascriptBuilder.Append("$(this).find('code').css('color', '#213EFF');");
                javascriptBuilder.Append("}");
                javascriptBuilder.Append("else {");
                javascriptBuilder.Append("$(this).find('code').css('color', '#00971B');");
                javascriptBuilder.Append("}");
                javascriptBuilder.Append("});");
                javascriptBuilder.Append("});");
                javascriptBuilder.Append("$(document).ready(function() {");
                javascriptBuilder.Append("$('li').click(function() {");
                javascriptBuilder.Append("var mycss = document.getElementById('themeLink');");
                javascriptBuilder.Append("if (mycss.getAttribute('href').indexOf('lesson_dark.css')>0)");
                javascriptBuilder.Append("{");
                javascriptBuilder.Append("$(this).find('code').css('color', '#213EFF');");
                javascriptBuilder.Append("}");
                javascriptBuilder.Append("else {");
                javascriptBuilder.Append("$(this).find('code').css('color', '#00971B');");
                javascriptBuilder.Append("}");
                javascriptBuilder.Append("});");
                javascriptBuilder.Append("});");
                javascriptBuilder.Append("$(document).ready(function() {");
                javascriptBuilder.Append("$('tr').click(function() {");
                javascriptBuilder.Append("var mycss = document.getElementById('themeLink');");
                javascriptBuilder.Append("if (mycss.getAttribute('href').indexOf('lesson_dark.css')>0)");
                javascriptBuilder.Append("{");
                javascriptBuilder.Append("$(this).find('code').css('color', '#213EFF');");
                javascriptBuilder.Append("}");
                javascriptBuilder.Append("else {");
                javascriptBuilder.Append("$(this).find('code').css('color', '#00971B');");
                javascriptBuilder.Append("}");
                javascriptBuilder.Append("});");
                javascriptBuilder.Append("});");
                javascriptBuilder.Append("$(document).ready(function() {");
                javascriptBuilder.Append("$('.panel').dblclick(function() {");
                javascriptBuilder.Append("var mycss = document.getElementById('themeLink');");
                javascriptBuilder.Append("if (mycss.getAttribute('href').indexOf('lesson_dark.css')>0)");
                javascriptBuilder.Append("{");
                javascriptBuilder.Append("$(this).find('code').css('color', '#213EFF');");
                javascriptBuilder.Append("}");
                javascriptBuilder.Append("else {");
                javascriptBuilder.Append("$(this).find('code').css('color', '#00971B');");
                javascriptBuilder.Append("}");
                javascriptBuilder.Append("});");
                javascriptBuilder.Append("});");
                javascriptBuilder.Append("</script>");
                #endregion
            }

            List<string> blockStrings = new List<string>();

            int index1 = 0;
            int index2 = htmlString.IndexOf("<h1");

            while (index2 >= 0)
            {
                blockStrings.Add(htmlString.Substring(index1, index2 - index1));
                index1 = index2;

                index2 = htmlString.IndexOf("<h1", index2 + 1);
            }

            if (index1 >= 0)
                blockStrings.Add(htmlString.Substring(index1));

            if (blockStrings.Count > 0)
            {
                #region 添加折叠一级标题的js代码

                StringBuilder sBuilder = new StringBuilder();

                int panelCount = 0;

                if (blockStrings.Count > 1)//第一个成员不是以<H1开头的。
                {
                    javascriptBuilder.Append("<script>");

                    for (int i = 1; i <= blockStrings.Count; i++)
                    {
                        String s = blockStrings[i - 1];

                        if (s.StartsWith("<h1") == false)
                        {
                            sBuilder.Append(s);
                            continue;
                        }

                        int indexOfH1End = s.IndexOf("</h1>");
                        if (indexOfH1End < 0)
                        {
                            sBuilder.Append(s);
                            continue;
                        }

                        sBuilder.Append("<h1 ");
                        sBuilder.Append("id=\"title" + i + "\"");
                        sBuilder.Append(s.Substring(3, indexOfH1End + 2));
                        sBuilder.Append("<div ");
                        sBuilder.Append("id=\"panel" + i + "\">");
                        sBuilder.Append(s.Substring(indexOfH1End + 5));
                        sBuilder.Append("</div>");

                        javascriptBuilder.Append("$(document).ready(function(){"
                                + "$(\"#title" + i + "\").click(function(){"
                                + "$(\"#panel" + i + "\").toggle();});});");

                        if (collapseH1)
                        {
                            javascriptBuilder.Append("$(document).ready(function(){"
                                    + "$(\"#panel" + i + "\").toggle();});");
                        }

                        panelCount++;
                    }
                }
                #endregion

                if (panelCount > 0)
                {
                    return javascriptBuilder + "</script>" + sBuilder.ToString();
                }

                return javascriptBuilder + htmlString;
            }
            else
                return javascriptBuilder + htmlString;
        }

        internal static string GetHeaderOfTodolistItem(string line)
        {
            if (string.IsNullOrWhiteSpace(line)) return "";

            var tmp = line.Replace(" ", "").Replace("－", "-").Replace("　", "").Replace("\t", "");//括弧间允许空白字符存在，所以不用TrimStart()。
            if (tmp.ToLower().StartsWith("[-]") || tmp.ToLower().StartsWith("[%]") ||
                tmp.ToLower().StartsWith("[+]") || tmp.ToLower().StartsWith("[#]"))
            {
                if (tmp.Length >= 4)
                {
                    if (tmp[3] == '(')
                    {
                        if (tmp.Substring(4).Contains(')'))
                        {
                            //[]()形如这样是文字链接。
                            return "";
                        }
                    }
                }

                return tmp.Substring(0, 3) + " ";
            }
            return "";
        }

        internal static string GetContentOfTodolistItem(string line)
        {
            if (string.IsNullOrWhiteSpace(line)) return "";

            var tmp = line.Replace(" ", "").Replace("－", "-").Replace("　", "").Replace("\t", "");//括弧间允许空白字符存在，所以不用TrimStart()。
            if (tmp.ToLower().StartsWith("[-]") || tmp.ToLower().StartsWith("[%]") ||
                tmp.ToLower().StartsWith("[+]") || tmp.ToLower().StartsWith("[#]"))
            {
                if (tmp.Length >= 4)
                {
                    if (tmp[3] == '(')
                    {
                        if (tmp.Substring(4).Contains(')'))
                        {
                            //[]()形如这样是文字链接。
                            return "";
                        }
                    }
                }

                return tmp.Substring(3).TrimStart(new char[] { ' ' });
            }
            return "";
        }

        /// <summary>
        /// 预处理markdown文本，这样可以支持表格、块引用预处理等。
        ///
        /// ｜表格标题｜ ｜列标题1｜列标题2｜列标题3｜
        /// ｜：－－－－：｜－－：｜：－－｜ 
        /// ｜列1、行1｜列2、行1｜列3、行1｜
        /// ｜列1、行2｜列2、行2｜列3、行3｜
        /// </summary>
        /// <param name="content"></param>
        /// <param name="separatorText"></param>
        /// <returns></returns>
        public static String PreProcessor(string content, char[] separatorText)
        {

            if (content == null || content.Length == 0)
                return content;

            content = Question.ConvertQuestionsToHtml(content);//先将试题文本转换为html标签。

            String[] lineStrings = content.Split(separatorText, StringSplitOptions.None);
            StringBuilder sb = new StringBuilder();
            for (int i = 0; i < lineStrings.Length; i++)
            {
                var text = lineStrings[i];
                int markIndex = -1;
                bool isBlockQuote = true;
                if (string.IsNullOrEmpty(text))
                {
                    isBlockQuote = false;
                }
                else
                {
                    for (int j = 0; j < text.Length; j++)
                    {
                        var c = text[j];
                        if (c == ' ' || c == '　') continue;
                        else if (c == '>' || c == '〉' || c == '》')
                        {
                            markIndex = j;
                            continue;
                        }

                        isBlockQuote = false;
                        break;
                    }
                }

                //格式化引用块
                if (isBlockQuote && markIndex >= 0 && markIndex <= 3)
                {
                    var ts = lineStrings[i].TrimStart(new char[] { '\t', ' ', '　' });
                    if (ts.StartsWith("》 ") || ts.StartsWith("》　") || ts.StartsWith("〉 ") || ts.StartsWith("〉　"))
                    {
                        lineStrings[i] = "> " + ts.Substring(2);
                    }
                    else if (ts.StartsWith("》") || ts.StartsWith("〉") || ts.StartsWith(">"))
                    {
                        lineStrings[i] = "> " + ts.Substring(1);
                    }
                }
            }

            content = sb.ToString();

            List<TableLinesInfo> tableSourcesList = new List<TableLinesInfo>();
            TableLinesInfo tlInfo = null;

            for (int i = 0; i < lineStrings.Length; i++)
            {
                String lineString = lineStrings[i];

                //表格行第一字符可以不是｜或|，但必须是非空字符。
                if (IsTableRow(lineString))
                {
                    if (lineString.StartsWith("|") == false && lineString.StartsWith("｜") == false)
                    {
                        lineString = "|" + lineString;
                    }

                    if (lineString.EndsWith("|") == false && lineString.EndsWith("｜") == false)
                    {
                        lineString = lineString + "|";
                    }
                }

                if (lineString.StartsWith("|") || lineString.StartsWith("｜"))
                // && (lineString.EndsWith("|") || lineString.EndsWith("｜")))
                {
                    if (tlInfo == null)
                    {
                        tlInfo = new TableLinesInfo();
                        tlInfo.setStartLineIndex(i);
                    }
                    tlInfo.Table.Add(lineString);
                }
                else
                {
                    if (tlInfo != null)
                    {
                        tlInfo.setEndLineIndex(i - 1);
                        tableSourcesList.Add(tlInfo);
                        tlInfo = null;
                    }
                }
            }

            // 如果最后一行就是表格最后一行
            if (tlInfo != null)
            {
                tlInfo.setEndLineIndex(lineStrings.Length - 1);
                tableSourcesList.Add(tlInfo);
                tlInfo = null;
            }

            for (int tableIndex = 0; tableIndex < tableSourcesList.Count; tableIndex++)
            {
                TableLinesInfo tableLinesInfo = tableSourcesList[tableIndex];
                
                int maxColumnsCount = 0;
                String definition = "";
                int definitionLineIndex = -1;

                bool hasColumnDefinitionLine = false;

                for (int j = 0; j < tableLinesInfo.Table.Count; j++)
                {
                    String lineString = tableLinesInfo.Table[j];

                    if (IsColumnDefinitionLine(lineString))
                    {
                        definition = lineString;
                        definitionLineIndex = j;

                        definition = definition.Replace("｜", "\r\n")
                                .Replace("|", "\r\n").Replace("：", ":")
                                .Replace("－", "-").Replace(" ", "")
                                .Replace("　", "").Replace("\t", "");


                        String[] definitionTexts = definition.Split(new char[2] { '\r', '\n' },
                            StringSplitOptions.RemoveEmptyEntries);

                        foreach (String definitionText in definitionTexts)
                        {
                            if (definitionText.Length == 0)
                                continue;

                            bool left = false;
                            bool right = false;
                            if (definitionText.StartsWith(":") || definitionText.StartsWith("^:"))
                                left = true;

                            if (definitionText.EndsWith(":") || definitionText.EndsWith(":^"))
                                right = true;

                            ColumnDefinition cDefinition;
                            if (left && right)
                            {
                                left = right = false;
                                cDefinition = ColumnDefinition.CENTER;
                            }
                            else if (right)
                            {
                                cDefinition = ColumnDefinition.RIGHT;
                            }
                            else
                            {
                                cDefinition = ColumnDefinition.LEFT;
                            }

                            tableLinesInfo.getColumnDefinitions().Add(cDefinition);
                        }

                        hasColumnDefinitionLine = true;
                    }
                    else
                    {
                        var commonLine = lineString.Replace("|", "\t").Replace("｜", "\t");
                        if (commonLine.EndsWith("\t"))
                        {
                            commonLine = commonLine.Substring(0, commonLine.Length - 1);
                        }

                        if (commonLine.StartsWith("\t"))
                        {
                            commonLine = commonLine.Substring(1);
                        }

                        String[] spanStrings = commonLine.Split('\t');
                        maxColumnsCount = Math.Max(maxColumnsCount, spanStrings.Length);
                    }

                }// 取出表的各列定义（主要是对齐）

                // 如果没有定义，则自动定义列（取最多的一行定义）。
                if (false == hasColumnDefinitionLine)
                {
                    for (int j = 0; j < tableLinesInfo.Table.Count; j++)
                    {
                        String lineString = tableLinesInfo.Table[j]
                                .Replace("|", "\t").Replace("｜", "\t");
                        if (lineString.EndsWith("\t"))
                        {
                            lineString = lineString.Substring(0, lineString.Length - 1);
                        }

                        if (lineString.StartsWith("\t"))
                        {
                            lineString = lineString.Substring(1);
                        }

                        String[] spanStrings = lineString.Split('\t');
                        
                        maxColumnsCount = Math.Max(maxColumnsCount, spanStrings.Length);
                    }

                    for (int i = 1; i <= maxColumnsCount; i++)
                    {
                        tableLinesInfo.getColumnDefinitions().Add(ColumnDefinition.LEFT);// 默认全左齐。
                    }
                }

                StringBuilder tableBuilder = new StringBuilder();
                tableBuilder.Append("<table>");

                for (int tableLineIndex = 0; (tableLineIndex < definitionLineIndex && tableLineIndex < tableLinesInfo
                        .Table.Count); tableLineIndex++)
                {
                    // 表头部分

                    String lineString = tableLinesInfo.Table[tableLineIndex]
                            .Replace(" ", "").Replace("　", "")
                            .Replace("\t", "");

                    if (lineString.StartsWith("|") || lineString.StartsWith("｜"))
                        lineString = lineString.Substring(1);

                    if (lineString.EndsWith("|") || lineString.EndsWith("｜"))
                        lineString.Substring(0, lineString.Length - 2);

                    lineString = lineString.Replace("｜", "\t").Replace("|", "\t");

                    // 去除头尾。
                    if (lineString.EndsWith("\t"))
                    {
                        lineString = lineString.Substring(0, lineString.Length - 1);
                    }

                    String[] spanStrings = lineString.Split('\t');

                    if (spanStrings.Length > 0)
                    {

                        if (tableLineIndex == 0 && spanStrings.Length == 1)
                        {
                            tableBuilder.Append("<caption>");
                            tableBuilder.Append(spanStrings[0]);
                            tableBuilder.Append("</caption>");
                            continue;
                        }

                        tableBuilder.Append("<tr>");
                        for (int spanIndex = 0; spanIndex < Math.Max(Math.Max(
                                spanStrings.Length, tableLinesInfo
                                        .getColumnDefinitions().Count), maxColumnsCount); spanIndex++)
                        {

                            if (spanIndex >= spanStrings.Length)
                            {
                                //感觉表头还是全部居中比较好看。
                                tableBuilder.Append("<th style=\"TEXT-ALIGN: center;\">");
                                tableBuilder.Append("</th>");
                                // 添加空单元格。

                                continue;// 防止用户一行末尾未填空内容。
                            }

                            string s = ConvertChar(spanStrings[spanIndex]);

                            //感觉表头还是全部居中对齐比较好看。
                            tableBuilder.Append("<th style=\"TEXT-ALIGN: center;\">");
                            tableBuilder.Append(s == "" ? "　" : s);
                            tableBuilder.Append("</th>");
                        }
                        tableBuilder.Append("</tr>");
                    }
                }

                for (int index = definitionLineIndex + 1; index < tableLinesInfo
                        .Table.Count; index++)
                {
                    // 表体部分

                    String lineString = tableLinesInfo.Table[index]
                            .Replace(" ", "").Replace("　", "").Replace("\t", "");

                    if (lineString.StartsWith("|") || lineString.StartsWith("｜"))
                        lineString = lineString.Substring(1);

                    if (lineString.EndsWith("|") || lineString.EndsWith("｜"))
                        lineString.Substring(0, lineString.Length - 2);

                    lineString = lineString.Replace("｜", "\t").Replace("|", "\t");

                    // 去除头尾。
                    if (lineString.EndsWith("\t"))
                    {
                        lineString = lineString.Substring(0, lineString.Length - 1);
                    }

                    String[] spanStrings = lineString.Split('\t');

                    if (spanStrings.Length > 0)
                    {
                        tableBuilder.Append("<tr>");
                        for (int spanIndex = 0; spanIndex < Math.Max(Math.Max(
                                spanStrings.Length, tableLinesInfo
                                        .getColumnDefinitions().Count), maxColumnsCount); spanIndex++)
                        {
                            if (spanIndex >= tableLinesInfo.getColumnDefinitions().Count)
                            {
                                tableBuilder.Append("<td>");
                            }

                            if (spanIndex >= spanStrings.Length)
                            {
                                // 要取出对齐
                                ColumnDefinition align = GetColumnAlign(
                                        tableLinesInfo.getColumnDefinitions(),
                                        spanIndex);

                                switch (align)
                                {
                                    case ColumnDefinition.CENTER:
                                        {
                                            tableBuilder
                                                    .Append("<td style=\"TEXT-ALIGN: center;\">");
                                            break;
                                        }
                                    case ColumnDefinition.RIGHT:
                                        {
                                            tableBuilder
                                                    .Append("<td style=\"TEXT-ALIGN: right;\">");
                                            break;
                                        }
                                    default:
                                        tableBuilder
                                                .Append("<td style=\"TEXT-ALIGN: left;\">");
                                        break;
                                }
                                tableBuilder.Append("　</td>");
                                // 添加空单元格。

                                continue;// 防止用户一行末尾未填空内容。
                            }

                            string s = ConvertChar(spanStrings[spanIndex]);
                            // 要取出对齐
                            ColumnDefinition align2 = GetColumnAlign(
                                    tableLinesInfo.getColumnDefinitions(),
                                    spanIndex);

                            switch (align2)
                            {
                                case ColumnDefinition.CENTER:
                                    {
                                        tableBuilder
                                                .Append("<td style=\"TEXT-ALIGN: center;\">");
                                        break;
                                    }
                                case ColumnDefinition.RIGHT:
                                    {
                                        tableBuilder
                                                .Append("<td style=\"TEXT-ALIGN: right;\">");
                                        break;
                                    }
                                default:
                                    tableBuilder
                                            .Append("<td style=\"TEXT-ALIGN: left;\">");
                                    break;
                            }
                            tableBuilder.Append(s == "" ? "　" : s);
                            tableBuilder.Append("</td>");
                        }
                        tableBuilder.Append("</tr>");
                    }
                }

                tableBuilder.Append("</table>");

                tableLinesInfo.setTableHtmlText(tableBuilder.ToString());
            }

            // 拼接。
            StringBuilder sbBuilder = new StringBuilder();
            for (int index = 0; index < lineStrings.Length; index++)
            {
                //处理删除线标记（代码块中不转变）。
                if (lineStrings[index].StartsWith("    ") == false && lineStrings[index].StartsWith("\t") == false)
                {
                    lineStrings[index] = lineStrings[index].Replace("[=", "<s>").Replace("[＝", "<s>").Replace("【＝", "<s>").Replace("【=", "<s>")
                        .Replace("=]", "</s>").Replace("＝】", "</s>").Replace("=】", "</s>").Replace("＝]", "</s>")
                        .Replace("[\\=", "[=").Replace("[\\＝", "[=").Replace("【\\＝", "[=").Replace("【\\=", "[=")
                        .Replace("=\\]", "=]").Replace("＝\\】", "=]").Replace("=\\】", "=]").Replace("＝\\]", "=]");
                }

                if (lineStrings[index].StartsWith(":") || lineStrings[index].StartsWith("："))
                {
                    //以冒号开头的，作为备注。
                    sbBuilder.Append($"<p class='comment'>{lineStrings[index].Substring(1)}</p>\r\n");
                    continue;
                }

                if (lineStrings[index].StartsWith("[-]") || lineStrings[index].StartsWith("[－]"))
                {
                    //以[-]开头的，作为尚未开始的任务列表项目。
                    sbBuilder.Append($"<span class='todo_u'>[-]</span> <span class='todo_up'>{lineStrings[index].Substring(4)}</span>\n");
                    continue;
                }

                if (lineStrings[index].StartsWith("[%]"))
                {
                    //以[%]开头的，作为正在进行的任务列表项目。
                    sbBuilder.Append($"<span class='todo_p'>[%]</span> <span class='todo_pp'>{lineStrings[index].Substring(4)}</span>\n");
                    continue;
                }

                if (lineStrings[index].StartsWith("[+]"))
                {
                    //以[+]开头的，作为已完成任务列表项目。
                    sbBuilder.Append($"<span class='todo_f'>[+]</span> <span class='todo_fp'><s>{lineStrings[index].Substring(4)}</s></span>\n");
                    continue;
                }

                if (lineStrings[index].StartsWith("[#]"))
                {
                    //以[#]开头的，作为已废弃任务列表项目。
                    sbBuilder.Append($"<span class='todo_a'>[#]</span> <span class='todo_ap'><s>{lineStrings[index].Substring(4)}</s></span>\n");
                    continue;
                }

                if (IsDateLine(lineStrings[index]))
                {
                    var className = "date";
                    var indexStatus = -1;
                    indexStatus = lineStrings[index].IndexOf("][-]");
                    if (indexStatus >= 0) className = "date_u";
                    else
                    {
                        indexStatus = lineStrings[index].IndexOf("][%]");
                        if (indexStatus >= 0)
                        {
                            className = "date_p";
                        }
                        else
                        {
                            indexStatus = lineStrings[index].IndexOf("][+]");
                            if (indexStatus >= 0)
                            {
                                className = "date_f";
                            }
                            else
                            {
                                indexStatus = lineStrings[index].IndexOf("][#]");
                                if (indexStatus >= 0)
                                {
                                    className = "date_a";
                                }
                            }
                        }
                    }

                    if (indexStatus >= 0)
                    {
                        sbBuilder.Append("<P><Span class='" + className + $"'>{lineStrings[index].Substring(0, indexStatus + 4)}</Span>{lineStrings[index].Substring(indexStatus + 5)}</P>");
                        continue;
                    }

                    var indexOfSquareBracket = lineStrings[index].IndexOf("]");
                    if (indexOfSquareBracket < 0)
                    {
                        sbBuilder.Append(lineStrings[index]);
                        continue;
                    }
                    else
                    {
                        sbBuilder.Append($"<P><Span class='date'>{lineStrings[index].Substring(0, indexOfSquareBracket + 1)}</Span>{lineStrings[index].Substring(indexOfSquareBracket + 1)}</P>");
                        continue;

                    }
                }

                TableLinesInfo tableLinesInfo = InWhitchTable(index,
                        tableSourcesList);

                if (null == tableLinesInfo)
                {
                    sbBuilder.Append(lineStrings[index] + separatorText[0]);
                    continue;
                }

                if (tableLinesInfo.isUsed())
                    continue;

                sbBuilder.Append(tableLinesInfo.getTableHtmlText());
                tableLinesInfo.setUsed(true);
            }

            return sbBuilder.ToString();
        }

        private static bool IsTableRow(string lineString)
        {
            if (string.IsNullOrEmpty(lineString)) return false;

            if (lineString.Contains("|") == false && lineString.Contains("｜") == false) return false;

            if (lineString.StartsWith("    ") || lineString.StartsWith("　　　　") || lineString.StartsWith("\t")) return false;

            var trim = lineString.Trim();
            if (trim.StartsWith("+ ") || trim.StartsWith("* ") || trim.StartsWith("- ")) return false;

            if (lineString.StartsWith(">") || lineString.StartsWith("》")) return false;

            return true;
        }

        /// <summary>
        /// 处理转义字符：
        /// \*
        /// \\
        /// \`
        /// \_
        /// \{
        /// \}
        /// \[
        /// \]
        /// \(
        /// \)
        /// \#
        /// \+
        /// \-
        /// \.
        /// \!
        /// </summary>
        /// <param name="src"></param>
        /// <returns></returns>
        public static string ConvertChar(string src)
        {
            src = src.Replace("\\*", "*").Replace("\\\\", "\\").Replace("\\`", "`")
                .Replace("\\_", "_").Replace("\\{", "{").Replace("\\}", "}").Replace("\\[", "[")
                .Replace("\\]", "]").Replace("\\(", "(").Replace("\\)", ")").Replace("\\#", "#")
                .Replace("\\+", "+").Replace("\\-", "-").Replace("\\.", ".").Replace("\\!", "!");
            return src;
        }

        private static ColumnDefinition GetColumnAlign(
            List<ColumnDefinition> columnDefinitions, int i)
        {
            if (i < 0 || i >= columnDefinitions.Count
                    || columnDefinitions.Count == 0)
                return ColumnDefinition.LEFT;

            return columnDefinitions[i];
        }

        private static TableLinesInfo InWhitchTable(int index,
            List<TableLinesInfo> tableLinesInfos)
        {
            foreach (TableLinesInfo t in tableLinesInfos)
            {
                if (index >= t.getStartLineIndex() && index <= t.getEndLineIndex())
                    return t;
            }

            return null;
        }

        /// <summary>
        /// 是否这样的定义文本：
        /// 
        /// ｜：－－－－：｜－－：｜：－－｜
        /// </summary>
        /// <param name="line"></param>
        /// <returns></returns>
        //private static bool IsColumnDefinitionLine(String line)
        //{

        //    if (line.Length == 0)
        //        return false;

        //    if (line.StartsWith("|") == false && line.StartsWith("｜") == false)
        //        return false;

        //    if (line.EndsWith("|") == false && line.EndsWith("｜") == false)
        //        return false;

        //    char[] lineChars = line.ToCharArray();
        //    foreach (char c in lineChars)
        //    {
        //        if (c != '|' && c != '｜' && c != ':' && c != '：' && c != ' '
        //                && c != '　' && c != '-' && c != '－' && c != '^' && c!='\r')
        //            return false;
        //    }

        //    return true;
        //}

        public static bool IsColumnDefinitionLine(string text)
        {
            if (string.IsNullOrEmpty(text)) return false;
            if (text.Contains("-") == false && text.Contains("－") == false) return false;//防止将空表行当作表的“列对齐定义行”。

            foreach (var c in text)
            {
                if (c == '-' || c == '－' || c == '|' || c == '｜' || c == ' '
                    || c == '　' || c == '\t' || c == ':' || c == '：' || c == '^')
                    continue;//^表示该列应自动顺序编号
                return false;
            }

            return true;
        }

        public static bool IsNumber(char c)
        {
            if ((c >= '1' && c <= '9') || c == '0') return true;
            if ((c >= '１' && c <= '９') || c == '０') return true;

            return false;
        }

        public static bool IsHorizontalLineDefinition(string s)
        {
            if (string.IsNullOrEmpty(s)) return false;
            if (s.StartsWith("\t")) return false;//以tab开头的是代码块<code>

            foreach (char c in s)
            {
                if (c != ' ' && c != '　' && c != '-' && c != '－' && c != '\t')//中间带tab符也算，但不能以tab符开头。
                    return false;
            }

            return true;
        }

        /// <summary>
        /// 判断此行文本是否只包含“｜”、“=”等。这样的行是“表格格式化功能”自动添加的分割表标题与表头行的装饰行。
        /// </summary>
        /// <param name="text"></param>
        /// <returns></returns>
        public static bool IsTableCaptionSplittor(string text)
        {
            if (string.IsNullOrEmpty(text)) return false;

            foreach (char c in text)
            {
                if (c == '=' || c == '｜' || c == '|' || c == '＝') continue;

                return false;
            }

            return true;
        }

        public enum TodoListItemState
        {
            NotTodoListItem, UnStart, Precessing, Finished,
            Aborted
        }

        public static string GetFormatedMarkdownText(string srcText)
        {
            string splitter;
            if (srcText.Contains("\r\n"))
            {
                splitter = "\r\n";
            }
            else if (srcText.Contains("\r"))
            {
                splitter = "\r";
            }
            else
            {
                splitter = "\n";  //默认都用\n
            }

            var lines = srcText.Split(new string[] { splitter }, StringSplitOptions.None);//注意：空行有用，引用和正常文本间的切换需要空行。

            var sb = new StringBuilder();
            for (int i = 0; i < lines.Length; i++)
            {
                var text = lines[i].Replace("\r", "").Replace("\n", "");
                var trimedText = text.TrimStart(new char[] { ' ', '　', '\t' });

                if (string.IsNullOrEmpty(trimedText))//保留空行。
                {
                    sb.Append(splitter);
                    continue;
                }

                if (IsDateLine(text))
                {
                    sb.Append(FormatDateLine(text));
                    sb.Append(splitter);
                    continue;
                }

                //代码块前的前导空格也是需要保留的。
                if (MarkDownEditorBase.IsStartWithTestPaperKeyWord(text) == false &&
                    text.StartsWith("    ") == false && text.StartsWith("\t") == false)
                {
                    text = trimedText;//试题元素前的全角空格需要保留。
                }

                //备注，必须顶格，否则易与Code块冲突
                if (IsCommentLine(text))
                {
                    //不再要求顶格，保留空格。全格式化成半角空格。
                    var commentStartIndex = 1;
                    var isCompileComment = false;
                    for (int iComment = 0; iComment < text.Length; iComment++)
                    {
                        var c = text[iComment];
                        if (c == ' ') continue;
                        else if (c == '　') continue;
                        else if (c == '\t') continue;
                        else if (c == '；' || c == ';')
                        {
                            commentStartIndex = iComment + 1;
                            break;
                        }
                        else if (c == '：' || c == ':')
                        {
                            commentStartIndex = iComment + 1;
                            isCompileComment = true;
                            break;
                        }
                    }

                    if (isCompileComment)
                    {
                        text = "：" + text.Substring(commentStartIndex);
                    }
                    else
                    {
                        text = "；" + text.Substring(commentStartIndex);
                    }
                }

                //任务列表，必须顶格，否则易与Code块冲突
                //形式是：[-][%][+][#]，减号表示未开始，%号表示正在进行，加号表示已完成，[#]表示废弃。
                //两个方括弧间可以有空格。
                if (IsTodoLine(text))
                {
                    TodoListItemState state = TodoListItemState.UnStart;
                    int index = text.IndexOf(']');
                    char[] trimArray = new char[] { ' ', '\t', '　' };
                    var t1 = text.Substring(0, index + 1).Trim(trimArray);
                    var t2 = text.Substring(index + 1);//跳过 ]

                    foreach (char c in t1)
                    {
                        if (c == '[' || c == ']' || c == ' ' || c == '\t' || c == '　') continue;//允许空格。
                        if (c == '-' || c == '－')
                        {
                            state = TodoListItemState.UnStart;
                            break;
                        }
                        else if (c == '%')
                        {
                            state = TodoListItemState.Precessing;
                            break;
                        }
                        else if (c == '+')
                        {
                            state = TodoListItemState.Finished;
                            break;
                        }
                        else
                        {
                            state = TodoListItemState.NotTodoListItem;
                            break;
                        }
                    }

                    switch (state)
                    {
                        case TodoListItemState.UnStart:
                            {
                                text = "[-] " + t2.TrimStart(trimArray);
                                break;
                            }
                        case TodoListItemState.Finished:
                            {
                                text = "[+] " + t2.TrimStart(trimArray);
                                break;
                            }
                        case TodoListItemState.Precessing:
                            {
                                text = "[%] " + t2.TrimStart(trimArray);
                                break;
                            }
                        case TodoListItemState.Aborted:
                            {
                                text = "[#] " + t2.TrimStart(trimArray);
                                break;
                            }
                        default:
                            {
                                break;//非任务列表项，直接返回原文本。
                            }
                    }
                }

                //文档总标题，总是顶格
                var htmlDocumentTitle = GetDocumentTitle(text);
                if (htmlDocumentTitle != null)
                {
                    text = text.TrimStart(' ', '\t', '　');
                    if (text.ToLower().StartsWith("title>"))
                    {
                        text = "%" + text.Substring(6);
                    }
                    else if (text.StartsWith("标题>"))
                    {
                        text = "%" + text.Substring(3);
                    }
                }

                //作者信息，总是顶格
                if (text.StartsWith("@") || text.StartsWith("·"))
                {
                    text = text.TrimStart(' ', '\t', '　');
                    text = "@" + text.Substring(1).Trim();
                }

                //如果是水平线，总是顶格
                if (IsHorizontalLineDefinition(text))
                {
                    text = text.Replace("　", "").Replace("\t", "").Replace("－", "-");
                }

                //代码块。注意★：代码块必须在备注、TodoList、页眉、页脚、文档标题之后，否则会冲突。
                //if ((text.StartsWith("    ") || text.StartsWith("\t")) &&
                //    (trimedText.StartsWith(">") == false && trimedText.StartsWith("》") == false &&
                //    trimedText.StartsWith("〉") == false))
                //{
                //    sb.Append(text);
                //    sb.Append(splitter);
                //    continue;
                //}
                //注意：原来即使以四个以上空格开头，只要除空格以外第一个字符是>，就会被解释为块引用——不会被编译为代码块。
                //但这样会造成语法冲突，所以还是当作代码块好了。
                if ((text.StartsWith("    ") || text.StartsWith("\t")))
                {
                    sb.Append(text);
                    sb.Append(splitter);
                    continue;
                }

                if (text.StartsWith("》") || text.StartsWith("〉") || text.StartsWith(">"))
                {
                    //引用是可以嵌套的，
                    int index = -1;
                    for (int j = 0; j < text.Length; j++)
                    {
                        char c = text[j];
                        if (c != ' ' && c != '　' && c != '》' && c != '〉' && c != '>')
                        {
                            index = j;
                            break;
                        }
                    }
                    //先找出第一个非引用格式字符的位置

                    if (index >= 0)
                    {
                        if (index >= text.Length)
                        {
                            text = "";//不允许空引用行的存在。
                        }
                        else
                        {
                            text = text.Substring(0, index).Replace("》", ">").Replace("〉", ">")
                                .Replace("　", "").Replace(" ", "").Replace(">", "> ") + text.Substring(index);
                        }
                    }
                    else
                    {
                        text = "";
                    }
                }

                #region 无序列表
                if (text.StartsWith("－") || text.StartsWith("-") || text.StartsWith("+"))
                {
                    //无序列表
                    int index = -1;
                    for (int j = 0; j < text.Length; j++)
                    {
                        char c = text[j];
                        if (c != ' ' && c != '　' && c != '-' && c != '－' && c != '+' && c != '*')
                        {
                            index = j;
                            break;
                        }
                    }

                    if (index >= 0)
                    {
                        if (index >= text.Length) text = "";//不允许空无序列表存在
                        else
                        {
                            text = text.Substring(0, index).Replace("-", "+").Replace("－", "+").Replace("　", "").Replace(" ", "").Replace("*", "+").Replace("+", "+ ") + text.Substring(index);
                        }
                    }
                    else
                    {
                        if (IsHorizontalLineDefinition(text) == false)//以-开头还可能是水平线，不能去除
                        {
                            text = "";
                        }
                        else
                        {
                            text = text.Replace("\t", "").Replace("－", "-").Replace("　", "").Replace(" ", "");
                        }
                    }
                }
                else if (text.StartsWith("* ") || text.StartsWith("*　"))
                {
                    //*号开头的比较复杂，因为会和加粗、倾斜冲突——所以必须在星号后跟一个空格才算。

                    //仍然是无序列表
                    int index = -1;
                    for (int j = 0; j < text.Length; j++)
                    {
                        char c = text[j];
                        if (c != ' ' && c != '　' && c != '-' && c != '－' && c != '+' && c != '*')
                        {
                            index = j;
                            break;
                        }
                    }

                    if (index >= 0)
                    {
                        if (index >= text.Length) text = "";//不允许空无序列表存在
                        else
                        {
                            text = text.Substring(0, index).Replace("-", "+").Replace("－", "+").Replace("　", "").Replace(" ", "").Replace("*", "+").Replace("+", "+ ") + text.Substring(index);
                        }
                    }
                    else
                    {
                        text = "";
                    }

                }//else... 否则只是加粗或倾斜。一行文本开头就加粗或倾斜是完全有可能的。

                #endregion

                #region 有序列表：

                if (text.Length >= 2 && IsNumber(text[0]))
                {
                    int index = -1;
                    for (int j = 0; j < text.Length; j++)
                    {
                        char c = text[j];
                        if (c != ' ' && c != '　' && IsNumber(c) == false && c != '.' && c != '、' && c != '．')
                        {
                            index = j;
                            break;
                        }
                    }

                    if (index > 1)//如果第1号索引位置就是非数字且非数字标记，必定不是有序列表。
                    {
                        if (index >= text.Length) text = "";//不允许空有序列表存在
                        else
                        {
                            text = text.Substring(0, index)
                                .Replace("１", "1").Replace("２", "2").Replace("３", "3").Replace("４", "4")
                                .Replace("５", "5").Replace("６", "6").Replace("７", "7").Replace("８", "8")
                                .Replace("９", "9").Replace("０", "0")
                                .Replace("　", "").Replace(" ", "").Replace("、", ".").Replace("．", ".").Replace(".", ". ")
                                + text.Substring(index);
                        }
                    }
                }

                #endregion

                #region 添加了单独的“表格格式化”功能，此处不需要再格式化，否则反而会冲突。
                //text = text.Replace("|", "｜");

                //if (text.Contains("|") || text.Contains("｜"))
                //{
                //    var lineContent = text.Trim(new char[] { ' ', '　', '\t' });
                //    if (lineContent.StartsWith("|") == false && lineContent.StartsWith("｜") == false)
                //    {
                //        text = "｜" + lineContent;
                //    }

                //    if (lineContent.EndsWith("|") == false && lineContent.EndsWith("｜") == false)
                //    {
                //        text += "｜";
                //    }
                //}

                //if (IsColumnDefinitionLine(text))
                //{
                //    text = text.Replace("：", ":").Replace("-", "－");
                //}
                #endregion

                sb.Append(text);
                sb.Append(splitter);
            }

            return sb.ToString();
        }


        /// <summary>
        /// 在编译之前，移除一些不必编译到html文件中的行。这些行包括：
        /// ⑴文档总标题行——以%（或%）开头——即全、半角的百分号；
        ///     ★这个比较特殊，只有第一个以%（或%）开头的会被当作标题，其它以%（或%）开头的行会原样参加编译。
        /// ⑵注释行——以;（或；）开头——即全半角分号；
        ///     ☆所有以;（或；）开头的行都会被忽略。
        /// ⑶文档页眉——以~开头。
        /// </summary>
        /// <returns></returns>
        public static string RemoveExtraLines(string srcText, out string htmlDocumentTitle,
           out string htmlDocumentPageHeader, out string footerText)
        {
            htmlDocumentPageHeader = htmlDocumentTitle = footerText = "";

            if (string.IsNullOrEmpty(srcText))
            {
                return string.Empty;
            }

            string[] lines;

            //尝试一下找“类型”，对填空题作些处理。
            //之前在做HistoryAssist的填空题时，一些格式与Markdown不兼容，这里处理一下就能用了。
            var startIndexOfType = srcText.IndexOf("类型＞＞");
            var endIndexOfType = srcText.IndexOf("＜＜类型");
            string typeText = "";
            if (startIndexOfType >= 0 && endIndexOfType >= 0 && endIndexOfType > startIndexOfType)
            {
                typeText = srcText.Substring(startIndexOfType + 4, endIndexOfType - startIndexOfType - 4);
            }

            if (typeText == "填空" || typeText == "填空题")
            {
                srcText = srcText.Replace("【", "`").Replace("】", "`")
                    .Replace("\n//", "\n;").Replace("\r//", "\n;");
            }

            lines = CustomMarkdownSupport.GetFormatedMarkdownText(srcText).Replace("\r\n", "\n")
                .Split(new char[] { '\r', '\n' }, StringSplitOptions.None);//要保留空行

            StringBuilder sb = new StringBuilder();
            bool removedDocumentTitleLine = false;
            bool removeFooterText = false;
            bool removePageHeader = false;

            //char[] trimChars = new char[] { '\t', ' ', '　' };
            //文档标题、备注都必须严格的在行首添加标记字符——否则易与code块冲突。

            for (int i = 0; i < lines.Length; i++)
            {
                string line = lines[i];

                if (removedDocumentTitleLine == false)
                {
                    var title = GetDocumentTitle(line);
                    if (title != null)
                    {
                        htmlDocumentTitle = title;
                        removedDocumentTitleLine = true;//只有第一个以%开头的行才会被当作文档标题。
                        sb.Append("\n");
                        continue;
                    }
                }

                if (IsPageHeader(line))
                {
                    if (removePageHeader == false)
                    {
                        htmlDocumentPageHeader = line.Substring(1).Trim();
                        removePageHeader = true;//只有第一个以~开头的行才会被当作页眉。
                        sb.Append("\n");
                        continue;
                    }
                }
                else if (IsDocumentAuthor(line))//即PageFooter
                {
                    if (removeFooterText == false)
                    {
                        footerText = line.Substring(1);
                        removeFooterText = true;
                        sb.Append("\n");
                        continue;
                    }
                }
                else if (IsCommentLine(line) && IsCompileCommentLine(line) == false)
                {
                    sb.Append("\n");
                    continue;
                    //以;（或；）号开头的行是注释行，不会被编译进html文档。
                }
                //任务列表原样输出！不再作为备注！2016年2月5日
                //else if (IsTodoLine(line))
                //{
                //    sb.Append("\n");
                //    continue;
                //    //以“[-]、[%]、[+]”开头的行也是注释，但是比较特殊，属于“任务列表”。
                //}
                else if (IsTableCaptionSplittor(line))
                {
                    //sb.Append("\n");//这个必须去除，否则表格的标题会与表格体分离——标题会成为一个单独的表格（只有一个单元格）
                    continue;
                }
                //下面这些也应去除。
                //＜＜＜信息＞
                // 标题＞＞明清君主专制制度的加强＜＜标题
                // 日期＞＞2013年4月14日＜＜日期
                // 作者＞＞杨震宇＜＜作者
                // 电邮＞＞historyassist@163.com＜＜电邮
                // 备注＞＞必修Ｉ，第一专题，第四节＜＜备注
                //＜信息＞＞＞
                else if (line.StartsWith("＜＜＜信息＞"))
                {
                    sb.Append("\n"); continue;
                }
                else if (line.StartsWith("　类型＞＞"))
                {
                    sb.Append("\n"); continue;
                }
                else if (line.StartsWith("　标题＞＞"))
                {
                    sb.Append("\n"); continue;
                }
                else if (line.StartsWith("　日期＞＞"))
                {
                    sb.Append("\n"); continue;
                }
                else if (line.StartsWith("　作者＞＞"))
                {
                    sb.Append("\n"); continue;
                }
                else if (line.StartsWith("　辑录＞＞"))
                {
                    sb.Append("\n"); continue;
                }
                else if (line.StartsWith("　电邮＞＞"))
                {
                    sb.Append("\n"); continue;
                }
                else if (line.StartsWith("　备注＞＞"))
                {
                    sb.Append("\n"); continue;
                }
                else if (line.StartsWith("　电邮＞＞"))
                {
                    sb.Append("\n"); continue;
                }
                else if (line.StartsWith("＜信息＞＞＞"))
                {
                    sb.Append("\n"); continue;
                }
                else
                {
                    //处理锚
                    //改造一下Markdown语法，
                    //按Markdown原始语法，[链接名](http://www.xxx.com)表示一个链接。
                    //但我这里的“锚”，会是这个样子[锚名](@锚ID)
                    //锚名可以为空，锚ID必须以#号开头——否则它就只是一个普通的链接。
                    var text = line;

                    if (text.StartsWith("    ") || text.StartsWith("\t"))
                    {
                        sb.Append(line);
                        sb.Append("\n");
                        continue;
                    }

                    if (text.Contains("[") == false || text.Contains("]") == false)
                    {
                        sb.Append(line);
                        sb.Append("\n");
                        continue;
                    }

                    if (text.Contains("(") == false && text.Contains("（") == false)
                    {
                        sb.Append(line);
                        sb.Append("\n");
                        continue;
                    }

                    if (text.Contains(")") == false && text.Contains("）") == false)
                    {
                        sb.Append(line);
                        sb.Append("\n");
                        continue;
                    }

                    text = text.Replace("[", "[[^]][")
                        .Replace("（", "(").Replace("）", ")")
                        .Replace(")", ")[[^]]").Replace("：", ":");

                    var spans = text.Split(new string[] { "[[^]]" }, StringSplitOptions.RemoveEmptyEntries);
                    var sb2 = new StringBuilder();
                    foreach (var s in spans)
                    {
                        var index = s.IndexOf("](@");
                        if (s.StartsWith("[") && s.EndsWith(")") && index > 0)
                        {
                            //此片段是一个锚
                            //例如：[abc](@abcde)
                            //abc是锚名，可以省略；
                            //abcde是锚的ID,不可省略。

                            var anchorName = s.Substring(1, index - 1);
                            var anchorID = s.Substring(index + 3, s.Length - index - 4)
                                .Replace("　", "_").Replace("\t", "_").Replace(" ", "_");

                            if (string.IsNullOrEmpty(anchorID))
                            {
                                sb2.Append(s);
                            }
                            else
                            {
                                //当锚ID不为空时，才算是个锚，才需要编译
                                //锚的名称可以为空。
                                //如果锚名为空，浏览编译后的html文档时，
                                //浏览器不会呈现这个锚——但是对这个锚的链接仍然有效。
                                sb2.Append("<span id=\"" + anchorID + "\" class=\"anchor\">" + anchorName + "</span>");
                            }
                        }
                        else sb2.Append(s);
                    }

                    line = sb2.ToString();
                }

                sb.Append(line);
                sb.Append("\n");
            }

            //尝试再找下标题
            if (string.IsNullOrEmpty(htmlDocumentTitle))
            {
                var startIndex = srcText.IndexOf("标题＞＞");
                var endIndex = srcText.IndexOf("＜＜标题");

                if (startIndex >= 0 && endIndex >= 0 && endIndex > startIndex)
                {
                    htmlDocumentTitle = srcText.Substring(startIndex + 4, endIndex - startIndex - 4);
                }
            }

            if (string.IsNullOrEmpty(footerText))
            {
                //脚注一般用于显示作者
                var startIndex = srcText.IndexOf("作者＞＞");
                var endIndex = srcText.IndexOf("＜＜作者");

                if (startIndex >= 0 && endIndex >= 0 && endIndex > startIndex)
                {
                    footerText = srcText.Substring(startIndex + 4, endIndex - startIndex - 4);
                }

                if (string.IsNullOrEmpty(footerText))
                {
                    startIndex = srcText.IndexOf("辑录＞＞");
                    endIndex = srcText.IndexOf("＜＜辑录");
                    if (startIndex >= 0 && endIndex >= 0 && endIndex > startIndex)
                    {
                        footerText = srcText.Substring(startIndex + 4, endIndex - startIndex - 4);
                    }
                }
            }

            return sb.ToString().Replace("\r", "");
        }

        public static bool IsTodoLine(string line)
        {
            if (string.IsNullOrWhiteSpace(line)) return false;
            if (line.StartsWith("    ") || line.StartsWith("\t")) return false;//这是代码块

            var tmp = line.Replace(" ", "").Replace("－", "-").Replace("　", "").Replace("\t", "");//括弧间允许空白字符存在，所以不用TrimStart()。
            if (tmp.ToLower().StartsWith("[-]") || tmp.ToLower().StartsWith("[%]") ||
                tmp.ToLower().StartsWith("[+]") || tmp.ToLower().StartsWith("[#]"))
            {
                if (tmp.Length >= 4)
                {
                    if (tmp[3] == '(')
                    {
                        if (tmp.Substring(4).Contains(')'))
                        {
                            //[]()形如这样是文字链接。
                            return false;
                        }
                    }
                }

                return true;
            }
            return false;
        }

        public static bool IsCommentLine(string line)
        {
            if (string.IsNullOrWhiteSpace(line)) return false;
            if (line.StartsWith("    ") || line.StartsWith("\t")) return false;//这是代码块

            var tmp = line.TrimStart(new char[] { ' ', '\t', '　' });
            if (tmp.StartsWith(";") || tmp.StartsWith("；") || tmp.StartsWith(":") || tmp.StartsWith("：")) return true;

            return false;
        }

        public static bool IsDateLine(string line)
        {
            if (string.IsNullOrWhiteSpace(line)) return false;
            if (line.StartsWith("    ") || line.StartsWith("\t")) return false;//这是代码块

            //Regex r = new Regex(@"^[ 　]{0,3}\[((((1[6-9]|[2-9]\d)\d{2})[-.,，。－、．/年](0?[13578]|1[02])[-.,，。－、．/月](0?[1-9]|[12]\d|3[01])日{0,1})|(((1[6-9]|[2-9]\d)\d{2})[-.,，。－、．/年](0?[13456789]|1[012])[-.,，。－、．/月](0?[1-9]|[12]\d|30)日{0,1})|(((1[6-9]|[2-9]\d)\d{2})[-.,，。－、．/年]0?2[-.,，。－、．/月](0?[1-9]|1\d|2[0-8])日{0,1})|(((1[6-9]|[2-9]\d)(0[48]|[2468][048]|[13579][26])|((16|[2468][048]|[3579][26])00))[-.,，。－、．/年]0?2-29[-.,，。－、．/月])日{0,1})\]");
            Regex r = new Regex(@"^[ 　]{0,3}\[\d{4,4}[-.,，。－、．/年][01]{0,1}\d[-.,，。－、．/月][0123]{0,1}\d日{0,1}([ 　]{1,}\d{1,2}[:：]\d{1,2}){0,1}\](\[[-%#\+－]\]){0,1}");
            var march = r.Match(line);
            if (march != null && march.Success)
            {
                var result = GetDateFromDateLine(line);
                if (result == null) return false;
                if (result.HasValue == false) return false;

                return true;
            }
            return false;
        }

        public static string FormatDateLineHeader(string header)
        {
            var trimChars = new char[] { ' ', '　', '\t' };
            return header.Replace("/", "-").Replace(".", "-").Replace(",", "-").Replace("，", "-")
                .Replace("。", "-").Replace("－", "-").Replace("、", "-").Replace("．", "-")
                .Replace("年", "-").Replace("月", "-").Replace("日", "").Replace("－", "-").Replace("：", ":").Trim(trimChars);
        }

        public static string FormatDateLine(string srcLine)
        {
            if (string.IsNullOrWhiteSpace(srcLine)) return "";
            var replacedLine = srcLine.Replace("－", "-");
            var index = replacedLine.IndexOf("]");
            if (index < 0) return srcLine;//原样返回

            string header;
            string tail;
            if (replacedLine.Length >= index + 2)
            {
                char c = replacedLine[index + 1];
                if (c == '-' || c == '－' || c == '%' ||
                    c == '#' || c == '+')
                {
                    header = srcLine.Substring(0, index + 1) + c;
                    tail = srcLine.Substring(index + 2);
                }
                else
                {
                    header = srcLine.Substring(0, index + 1);
                    tail = srcLine.Substring(index + 1);
                }
            }
            else
            {
                header = srcLine.Substring(0, index + 1);
                tail = srcLine.Substring(index + 1);
            }

            var trimChars = new char[] { ' ', '　', '\t' };
            //-.,，。－、．/

            var secHeaderMark = "";
            var tailTrim = tail.Trim(trimChars);
            if (tail.StartsWith("[-]") || tail.StartsWith("[－]") || tail.StartsWith("[%]") ||
                tail.StartsWith("[#]") || tail.StartsWith("[+]"))
            {
                secHeaderMark = tail.Substring(0, 3).Replace("[－]", "[-]");
                tail = tailTrim.Substring(3);
            }

            return " " + FormatDateLineHeader(header) + secHeaderMark + " " + tail.Trim(trimChars);

        }

        public static DateTime? GetDateFromDateLine(string dateLine)
        {
            if (string.IsNullOrWhiteSpace(dateLine)) return null;
            var index = dateLine.IndexOf("]");

            DateTime dt;
            if (index < 0)
            {
                if (DateTime.TryParse(dateLine, out dt)) return dt;

                return null;//原样返回
            }

            var header = dateLine.Substring(0, index + 1).Replace("[", "").Replace("]", "");

            if (DateTime.TryParse(header, out dt)) return dt;

            return null;
        }

        public static string GetDateLineStatusMark(string dateLine)
        {
            if (string.IsNullOrWhiteSpace(dateLine)) return "";

            Regex regex = new Regex(@"\]\[[ 　\t]*[-－+#%][ 　\t]*\]");
            var march = regex.Match(dateLine);
            if (march != null && march.Success)
            {
                return dateLine.Substring(march.Index + 1, march.Length - 1).Replace(" ", "").Replace("　", "").Replace("\t", "");
            }

            return "";
        }

        public static string GetTailTextOfDateLine(string dateLine)
        {
            if (string.IsNullOrWhiteSpace(dateLine)) return "";

            Regex regex = new Regex(@"\[[ 　\t]{0,}[-－%#+][ 　\t]{0,}\][ 　\t].*$");
            var march = regex.Match(dateLine);
            if (march != null && march.Success)
            {
                var index = march.Value.IndexOf("]");
                if (index < 0) return march.Value;
                return march.Value.Substring(index + 1).Trim(new char[] { ' ', '　', '\t' });
            }
            return "";
        }

        public static bool IsCompileCommentLine(string line)
        {
            if (string.IsNullOrWhiteSpace(line)) return false;
            if (line.StartsWith("    ") || line.StartsWith("\t")) return false;//这是代码块

            var tmp = line.TrimStart(new char[] { ' ', '\t', '　' });
            if (tmp.StartsWith(":") || tmp.StartsWith("：")) return true;

            return false;
        }

        public static string GetDocumentTitle(string line)
        {
            if (string.IsNullOrWhiteSpace(line)) return null;

            var text = line.Replace("　", " ");
            if (text.StartsWith("    ") || text.StartsWith("\t")) return null;
            //注意：[空格][Tab]不会被编译为Code行。

            var tmp = line.TrimStart(new char[] { ' ', '\t', '　' });
            if (line.StartsWith("%")) return tmp.Substring(1);

            var tmp2 = tmp.Replace("Ｔ", "t").Replace("ｔ", "t").Replace("Ｉ", "i").Replace("ｉ", "i").Replace("Ｌ", "l").Replace("ｌ", "l").Replace("Ｅ", "e").Replace("ｅ", "e")
                .Replace("〉", ">").ToLower();
            if (tmp2.StartsWith("title>")) return tmp.Substring(6);

            if (tmp.StartsWith("标题>") || tmp.StartsWith("标题〉")) return tmp.Substring(3);

            return null;
        }

        public static bool IsDocumentAuthor(string line)
        {
            if (string.IsNullOrWhiteSpace(line)) return false;

            var text = line.Replace("　", " ");
            if (text.StartsWith("    ") || text.StartsWith("\t")) return false;
            //注意：[空格][Tab]不会被编译为Code行。

            var tmp = line.TrimStart(new char[] { ' ', '\t', '　' });
            if (tmp.StartsWith("@") || tmp.StartsWith("·")) return true;

            return false;
        }

        public static bool IsPageHeader(string line)
        {
            if (string.IsNullOrWhiteSpace(line)) return false;

            var text = line.Replace("　", " ");
            if (text.StartsWith("    ") || text.StartsWith("\t")) return false;
            //注意：[空格][Tab]不会被编译为Code行。

            var tmp = line.TrimStart(new char[] { ' ', '\t', '　' });
            if (tmp.StartsWith("~")) return true;

            return false;
        }

        private static bool IsEncripied(string fullFilePath)
        {
            if (String.IsNullOrWhiteSpace(fullFilePath) || File.Exists(fullFilePath) == false) return false;

            System.IO.StreamReader s = File.OpenText(fullFilePath);
            var fstEncriptLine = s.ReadLine();
            if (string.IsNullOrEmpty(fstEncriptLine)) return false;

            var fstDecriptedLine = SetPasswordPanel.TextDecrypt(fstEncriptLine.Replace("[[<r>]]", "\r").Replace("[[<n>]]", "\n").ToCharArray(), "DyBj#PpBb");
            if (fstDecriptedLine.Contains("Password:") && fstDecriptedLine.Contains("Question:") && fstDecriptedLine.Contains("|"))
            {
                return true;
            }

            return false;
        }

        /// <summary>
        /// 
        /// </summary>
        /// <param name="srcFilePath"></param>
        /// <param name="destFilePath"></param>
        /// <param name="formatBeforeCompile"></param>
        /// <returns>如果编译成功，返回新文件路径。</returns>
        public static string Compile(string srcFilePath, bool collapseH1, ref string htmlTitle)
        {
            string shortFileName;
            int lastIndex = srcFilePath.LastIndexOf("\\");
            if (lastIndex >= 0)
            {
                string name = srcFilePath.Substring(lastIndex + 1);
                if (string.IsNullOrEmpty(name))
                {
                    shortFileName = "unnamed.md";
                }
                else shortFileName = name;
            }
            else
            {
                shortFileName = "unnamed.md";
            }

            string htmlDocumentTitle;
            string htmlDocumentPageHeader;
            string footerText;

            MarkdownSharp.Markdown md = new MarkdownSharp.Markdown();

            string contentText;

            bool isEncrypted = false;
            string password, passwordTip;
            isEncrypted = IsFileEncrypted(srcFilePath, out password, out passwordTip);

            if (isEncrypted)
            {
                string result = null;
                foreach (UIElement ue in Globals.MainWindow.mainTabControl.Items)
                {
                    var edit = ue as MarkdownEditor;
                    if (edit != null && string.IsNullOrEmpty(edit.FullFilePath) == false &&
                        edit.FullFilePath.ToLower() == srcFilePath.ToLower())
                    {
                        if (edit.InputPasswordPanel.Visibility == Visibility.Collapsed)
                        {
                            result = edit.Password;
                        }
                    }
                }

                if (result == null)
                {
                    result = InputBox.Show(Globals.AppName, $"{passwordTip}", "", false, "注意：密码区分大小写。", true);
                }

                if (result == null)
                {
                    return "";
                }
                else
                {
                    if (result == password)
                    {
                        var allText = File.ReadAllText(srcFilePath);
                        contentText = SetPasswordPanel.TextDecrypt(allText.Substring(allText.IndexOf("\r\n") + 2).ToCharArray(), "DyBj#PpBb");
                    }
                    else
                    {
                        LMessageBox.Show("密码错误，无法编译！", Globals.AppName,
                            MessageBoxButton.OK, MessageBoxImage.Warning);
                        return null;
                    }
                }
            }
            else
            {
                contentText = File.ReadAllText(srcFilePath);
            }

            string htmlBody = RemoveExtraLines(contentText, out htmlDocumentTitle, out htmlDocumentPageHeader, out footerText);

            htmlBody = CustomMarkdownSupport.PreProcessor(htmlBody, new char[] { '\n' });

            if (string.IsNullOrEmpty(htmlDocumentTitle))
            {
                htmlDocumentTitle = "Markdown Document";
            }

            htmlBody = md.Transform(htmlBody);

            string directoryLevelMark = GetDirectoryLevelMark(srcFilePath);

            htmlBody = CustomMarkdownSupport.AppendProcessor(htmlBody, directoryLevelMark, collapseH1, Globals.MainWindow.CompileCodeToFillBlank);
            htmlBody = htmlBody.Replace("<p><div ", "<div ").Replace("</div></p>", "</div>");//去除不必要的空段。

            if (string.IsNullOrEmpty(htmlBody) == false)
            {
                String linkCss2 = "<link id='themeLink' rel=\"stylesheet\" href=\"" + directoryLevelMark
                    + "lesson_" + Globals.MainWindow.ColorText + ".css\" type=\"text/css\">";

                string script = "<script>"
                                + "function ShowPage() "
                                + "{"
                                + "document.getElementById('content').style.visibility = \"visible\";"
                                + "}"
                                + "function HidePage()"
                                + "{"
                                + "document.getElementById('content').style.visibility = \"hidden\";"
                                + "}"
                                + "</script>";

                string pagefotterString = "<div class=\"foot\"><p id=\"author\">" + footerText //这是文档的脚注文本，一般可以填组织、作者名称什么的。
                    + "</p><p id=\"compile_time\">" + DateTime.Now.ToString() + "</p></div>";

                var charset = Globals.MainWindow.DefaultEncoding;//"gb2312";//"utf-8";

                string htmlDocumentText = "<!DOCTYPE html><html xmlns=\"http://www.w3.org/1999/xhtml\"><head><title>"
                                + htmlDocumentTitle
                                + "</title><meta name=\"info\" content=\""
                                + shortFileName
                                + "\" >"
                                + "<meta name=\"viewport\" content=\"width=device-width, user-scalable=yes\" />"
                                + "<meta http-equiv=\"Content-Type\" content=\"text/html; charset=" + charset + "\">"
                                + linkCss2
                                + script
                                //+ "</head><body onLoad=\"HidePage()\">"
                                + "</head><body><div style=\"text-align:right\"><span class=\"pageheader\">"
                                + htmlDocumentPageHeader
                                + "</span><a onclick=\"print()\" style=\"visibility: hidden\">打印〔此功能会丢失内容〕...</a></div>"
                                 + "<div id=\"printArea\">"
                                + "<p class='fileheader'>"
                                + htmlDocumentTitle
                                //这是整个文档的标题，以%开头。
                                + "</p><hr />"
                                + "<div id=\"content\">"
                                + htmlBody
                                + "</div><br /><br /><br /><br /><br /><hr />"
                                + pagefotterString + "</div>"
                                + "</body></html>";

                htmlTitle = htmlDocumentTitle;

                var newFileName = srcFilePath.Substring(0, srcFilePath.Length - 3) + ".html";

                if (charset == "utf-8")
                {
                    using (StreamWriter stream = new StreamWriter(newFileName, false, Encoding.UTF8))
                    {
                        stream.Write(htmlDocumentText);
                    }
                }
                else if (charset == "gb2312")
                {
                    using (StreamWriter stream = new StreamWriter(newFileName, false, System.Text.Encoding.GetEncoding("gb2312")))
                    {
                        stream.Write(htmlDocumentText);
                    }
                }

                return newFileName;
            }

            return null;
        }

        public static bool IsFileEncrypted(string srcFilePath, out string password, out string passwordTip)
        {
            var fstLine = new StreamReader(srcFilePath).ReadLine();

            var decryptedFstLine = SetPasswordPanel.TextDecrypt(
                fstLine.Replace("[[<r>]]", "\r").Replace("[[<n>]]", "\n").ToCharArray(), "DyBj#PpBb");
            var indexOfPassword = decryptedFstLine.IndexOf("Password:");
            var indexOfQuestion = decryptedFstLine.IndexOf("Question:");

            password = "";
            passwordTip = "";
            if (indexOfQuestion >= 0 && indexOfPassword >= 10)
            {
                password = decryptedFstLine.Substring(indexOfPassword + 9);
                passwordTip = decryptedFstLine.Substring(9, indexOfPassword - 10);
                return true;
            }

            return false;
        }

        public static int GetSubLayerOfDirectory(string fullFilePath)
        {
            if (string.IsNullOrEmpty(fullFilePath)) return -1;

            if (fullFilePath.ToLower().StartsWith(Globals.PathOfWorkspace.ToLower()) == false) return -1;

            var tailTextOfPath = fullFilePath.Substring(Globals.PathOfWorkspace.Length);

            int i = 0;
            foreach (char c in tailTextOfPath)
            {
                if (c == '\\')
                {
                    i++;
                }
            }
            return i;
        }

        public static string GetDirectoryLevelMark(string fullFilePath)
        {
            StringBuilder sb = new StringBuilder();

            for (int i = 0; i <= GetSubLayerOfDirectory(fullFilePath) - 1; i++)
            {
                sb.Append("../");
            }
            return sb.ToString();
        }

    }
}
